Skip to content

✨ Feat: Add SPIRE x5c init-container signing, trust-domain binding, and ConfigMap trust bundle - #185

Merged
usize merged 13 commits into
rossoctl:mainfrom
kevincogan:feat/spire-init-container
Feb 25, 2026
Merged

✨ Feat: Add SPIRE x5c init-container signing, trust-domain binding, and ConfigMap trust bundle#185
usize merged 13 commits into
rossoctl:mainfrom
kevincogan:feat/spire-init-container

Conversation

@kevincogan

Copy link
Copy Markdown
Contributor

Summary

Implements automated AgentCard signing via a SPIRE init-container and replaces the legacy signature verification infrastructure with an X.509 certificate chain (x5c) based approach.

Key changes:

  • Remove deprecated providers: Drop SecretProvider, JWKSProvider, NoOpProvider, legacy samples, unused images, and helper scripts.

  • Add X5CProvider: X.509 certificate chain-based JWS signature verification with SPIFFE ID extraction from SAN URIs, trust bundle caching via ConfigMap, chain depth limits, and automatic bundle refresh.

  • Refactor AgentCard controller: Replace allowedSpiffeIDs manual allowlist with trust domain only identity binding derived from x5c certificate SAN URIs. Add proactive SVID expiry restart logic with bundle-hash and resign-trigger annotations.

  • Add agentcard-signer init-container: Binary that fetches X.509-SVID from SPIRE Workload API, signs AgentCard JSON with JWS (x5c header), and writes the signed card to a shared volume.

  • Wire operator flags: Add CLI flags for --spire-trust-bundle-configmap, --spire-trust-domain, and --svid-expiry-grace-period with startup validation. Harden fetcher with io.LimitReader (1 MiB cap).

  • Update Helm chart and CRDs: Regenerate CRD schemas removing allowedSpiffeIDs, update manager deployment args for ConfigMap trust bundle and SVID grace period.

  • Update documentation: Revise signature verification, identity binding, architecture, and API reference docs for the new x5c/SPIRE model.

Related issue(s)

Fixes #179
Related #116

(Optional) Testing Instructions

  1. Unit tests — all pass.
  2. End-To-End Testing: all pass.

Drop SecretProvider, JWKSProvider, NoOpProvider and their tests.
Remove legacy samples (build pipelines, JWKS signing), unused images,
and helper scripts that are superseded by the SPIRE init-container
approach.

Signed-off-by: Kevin Cogan <kevin.s.cogan@gmail.com>
Implement X5CProvider for X.509 certificate chain-based JWS signature
verification with SPIFFE ID extraction, trust bundle caching via
ConfigMap, chain depth limits, and automatic bundle refresh. Remove
legacy provider wiring and update metrics.

Signed-off-by: Kevin Cogan <kevin.s.cogan@gmail.com>
…inding

Replace allowedSpiffeIDs allowlist with trust-domain-only identity
binding derived from x5c certificate SAN URIs. Add proactive SVID
expiry restart logic, trust bundle hash tracking, and resign-trigger
annotations. Update tests for new verification and binding flows.

Signed-off-by: Kevin Cogan <kevin.s.cogan@gmail.com>
Implement init-container binary that fetches X.509-SVID from SPIRE
Workload API, signs AgentCard JSON with JWS (x5c header), and writes
the signed card to a shared volume. Includes Dockerfile for distroless
image build.

Signed-off-by: Kevin Cogan <kevin.s.cogan@gmail.com>
Add CLI flags for ConfigMap trust bundle, SPIRE trust domain, and SVID
expiry grace period with startup validation. Cap HTTP response body at
1 MiB via io.LimitReader. Remove AllowedSpiffeIDs from CRD types and
update deepcopy. Add go-spiffe/v2 dependency.

Signed-off-by: Kevin Cogan <kevin.s.cogan@gmail.com>
Update manager deployment args for ConfigMap trust bundle and SVID
grace period. Regenerate CRD schemas removing allowedSpiffeIDs. Add
signed-agent-example sample demonstrating full x5c signing flow.

Signed-off-by: Kevin Cogan <kevin.s.cogan@gmail.com>
Revise signature verification docs for ConfigMap trust bundle. Update
architecture and API reference for x5c provider and removed providers.
Refresh identity binding quickstart for trust-domain-only model.

Signed-off-by: Kevin Cogan <kevin.s.cogan@gmail.com>
Accepts upstream removal of the Agent CRD and related controllers,
types, RBAC, and samples. Retains AgentCard identity binding,
signature verification, and SPIRE trust bundle integration.
Restores configmaps and events RBAC permissions required by the
X5CProvider and event recorder.

Signed-off-by: Kevin Cogan <kevin.s.cogan@gmail.com>
When a manually-created AgentCard targets the same workload as an
auto-created card, the sync controller now detects the supersession
and deletes its auto-created card, ensuring exactly one card per
workload. Adds unit tests and updates E2E guide documentation.

Signed-off-by: Kevin Cogan <kevin.s.cogan@gmail.com>
…ates

LastSyncTime, BindingStatus.LastEvaluationTime, and condition
LastTransitionTime were written on every reconcile, causing the status
object to change each cycle and re-triggering the controller. Update
timestamps only when the underlying data actually changes:

- LastSyncTime: only when cardId changes (or on first sync)
- LastEvaluationTime: only when Bound/Reason/Message changes
- LastTransitionTime: delegate to meta.SetStatusCondition (updates
  only on Status/Reason transitions)

Signed-off-by: Kevin Cogan <kevin.s.cogan@gmail.com>
@usize
usize self-requested a review February 25, 2026 05:17

@usize usize left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: looks like we need to run go fmt

And finally, a request. This looks great, but the sample-config might confuse folks. I cheated and asked an LLM to help me hone in on the details:

  1. image: weather-agent:latest — this image doesn't exist in any registry. Should this be
  ghcr.io/kagenti/agent-examples/weather_service:v0.0.1-alpha.3 (as used in GETTING_STARTED.md), or does it need a real static-file-server
  image that serves /.well-known/agent.json?
  2. image: kagenti/agentcard-signer:latest — not published anywhere. It needs to be built from cmd/agentcard-signer/ and loaded into the
  cluster, but there are no instructions for this.
  3. No namespace creation — everything targets namespace: agents but nothing creates it (not in the manifests or the kustomization).
  4. Missing ClusterSPIFFEID — the a2a-signature-verification.md doc lists this as a prerequisite (step 1), but it's not included in the
  sample. Without it the init-container will crash because SPIRE won't issue an SVID.
  5. Operator flag configuration not covered — the operator needs --require-a2a-signature=true and --spire-trust-domain=<domain> to verify
  signatures, but there's no guidance on how to set these (e.g., Helm values).

The basic idea, is that I think we need to be explicit about e.g. requiring a kagenti dev install via kagenti/deployments/run_install.sh --env dev (in the Kagenti repo) and to add to the makefile in the project to build and add the agent signature container locally maybe? Or we could do it with a setup.sh and teardown.sh script.

That's the approach I've been taking e.g., here in AuthBridge in this demo I added: https://github.com/kagenti/kagenti-extensions/tree/main/AuthBridge/demos/multi-target

If we could add a working demo like that, that works with a fresh dev install of kagenti to a local kind cluster it would likely be a big help and we could use it to demo the cool new feature.

Otherwise, this is great. Ready to approve after hearing your thoughts about adding a slightly easier-to-run demo.

```bash
# Generate a different key pair
openssl genrsa -out wrong-private-key.pem 2048
See `config/samples/spire-signing/` for complete manifests. The key elements:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think this is now config/samples/signed-agent-example ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the thorough review! All points addressed and a lot cleaner now:

  1. go fmt: Fixed across all changed files.

  2. Sample config issues: Removed config/samples/signed-agent-example/ entirely and replaced it with four self-contained demos in demos/ following the AuthBridge convention (demo.md + run-demo-commands.sh + teardown-demo.sh + k8s/ manifests):

    • agentcard-spire-signing: Full init-container signing flow with namespace, ClusterSPIFFEID, and operator flag documentation

    • agentcard-enforcement: Trust domain rejection and NetworkPolicy enforcement

    • agentcard-auto-discovery: Sync controller auto-creating AgentCards for labeled workloads

    • agentcard-proactive-restart: SVID expiry detection triggering automatic re-signing

      Each demo is runnable end-to-end against a fresh kagenti/deployments/run_install.sh --env dev install. Added make build-signer and make load-signer-image targets for the init-container image.

  3. Stale path: Fixed. The old docs are replaced with agentcard-signature-verification.md and agentcard-identity-binding.md, all cross-references updated.

    Also cleaned up dead Secret setup in the signature verification tests (leftover from the removed SecretProvider).

Add four runnable demos with setup/run/teardown scripts and k8s
manifests: spire-signing, enforcement, auto-discovery, and
proactive-restart. Add build-signer and load-signer-image Makefile
targets for the agentcard-signer init-container image.

Signed-off-by: Kevin Cogan <kevin.s.cogan@gmail.com>
Rename a2a-signature-verification.md to agentcard-signature-verification.md
and identity-binding-quickstart.md to agentcard-identity-binding.md for
naming consistency. Remove config/samples/signed-agent-example/ (superseded
by demos/agentcard-spire-signing/). Update cross-references in
GETTING_STARTED, api-reference, architecture, and dev docs. Fix stale
provider description in dev.md.

Signed-off-by: Kevin Cogan <kevin.s.cogan@gmail.com>
Apply gofmt formatting fixes across controller and signature packages.
Remove unused Secret creation and cleanup from signature verification
tests (leftover from removed SecretProvider).

Signed-off-by: Kevin Cogan <kevin.s.cogan@gmail.com>
@kevincogan
kevincogan requested a review from usize February 25, 2026 13:09
@usize
usize merged commit 0fdc33e into rossoctl:main Feb 25, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feature: SPIRE init-container for automated AgentCard signing at deploy time

2 participants